﻿
//	Procedural Equirectangular Textures
//	Polka Dots Pattern
//
//	defaults = {...}	- default parameters
//	pattern( ... )		- calculate color of pixel
//	texture( params )	- generate a texture
//	material( ... )		- material shader fix



import { Vector3, Color, PolyhedronGeometry, TetrahedronGeometry, OctahedronGeometry, DodecahedronGeometry, IcosahedronGeometry, MathUtils } from "three";
import { mergeVertices } from 'three/addons/utils/BufferGeometryUtils.js';
import { retexture, map } from "../texture-generator.js";



// internal class to have the cube as one of the Platonic solids

class HexahedronGeometry extends PolyhedronGeometry {

	constructor( radius, level ) {

		var verticesOfCube = [
			-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
			-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1,
		];

		var indicesOfFaces = [
			2, 1, 0, 0, 3, 2,
			0, 4, 7, 7, 3, 0,
			0, 1, 5, 5, 4, 0,
			1, 2, 6, 6, 5, 1,
			2, 3, 7, 7, 6, 2,
			4, 5, 6, 6, 7, 4
		];

		super( verticesOfCube, indicesOfFaces, radius, level );

	}

}



// generate predefined layouts of points on a unit sphere

var layouts = [
	{ geometry: TetrahedronGeometry, level: 0, points: [], maxScale: 106 },	// #2, pts=4

	{ geometry: OctahedronGeometry, level: 0, points: [], maxScale: 95 },		// #3, pts=6
	{ geometry: OctahedronGeometry, level: 1, points: [], maxScale: 77 },		// #6, pts=18

	{ geometry: HexahedronGeometry, level: 0, points: [], maxScale: 96 },		// #4, pts=8
	{ geometry: HexahedronGeometry, level: 1, points: [], maxScale: 69 },		// #8, pts=26

	{ geometry: DodecahedronGeometry, level: 0, points: [], maxScale: 79 },	// #7, pts=20

	{ geometry: IcosahedronGeometry, level: 0, points: [], maxScale: 79 },	// #5, pts=12
	{ geometry: IcosahedronGeometry, level: 1, points: [], maxScale: 59 },	// #9, pts=42
	{ geometry: IcosahedronGeometry, level: 2, points: [], maxScale: 48 },	// #10, pts=92
	{ geometry: IcosahedronGeometry, level: 3, points: [], maxScale: 42 },	// #11, pts=162
	{ geometry: IcosahedronGeometry, level: 4, points: [], maxScale: 37 },	// #12, pts=252
	{ geometry: IcosahedronGeometry, level: 5, points: [], maxScale: 32 },	// #13, pts=361
	{ count: 1, points: [], maxScale: 141 }, // #1, pts=1
	{ count: 500, points: [], maxScale: 33 }, // #14, pts=500
	{ count: 750, points: [], maxScale: 30 }, // #15, pts=750
	{ count: 1000, points: [], maxScale: 28 }, // #16, pts=1000
	{ count: 1500, points: [], maxScale: 25 }, // #17, pts=1500
	{ count: 2000, points: [], maxScale: 23 }, // #18, pts=2000
	{ count: 3000, points: [], maxScale: 21 }, // #19, pts=3000
	{ count: 5000, points: [], maxScale: 18 }, // #20, pts=5000
];


// generate dot positions

for ( var index=0; index<layouts.length; index++ ) {

	var level = layouts[ index ].level,
		geometryClass = layouts[ index ].geometry;

	if ( geometryClass ) {

		// platonic
		var geometry = new geometryClass( 1, level );
		geometry.deleteAttribute( 'normal' );
		geometry.deleteAttribute( 'uv' );

		var	mergedGeometry = mergeVertices( geometry );

		var positions = mergedGeometry.getAttribute( 'position' );

		for ( var i=0; i<positions.count; i++ )
			layouts[ index ].points.push( new Vector3().fromBufferAttribute( positions, i ) );

		geometry.dispose( );
		mergedGeometry.dispose( );

	} else {

		// fibonaccic
		var n = layouts[ index ].count;

		var gr = ( 1+5**0.5 )/2;

		for ( var i=0; i<n; i++ ) {

			var theta = 2*Math.PI*i/gr;
			var phi = Math.acos( 1-( 2*i+1 )/n );
			layouts[ index ].points.push( new Vector3().setFromSphericalCoords( 1, phi, theta ) );

		}

	}

}

layouts.sort( ( a, b )=>a.points.length-b.points.length );



var defaults = {
	$name: 'Polka dots',
	$layouts: layouts,

	width: 512,
	height: 256,

	layout: 9,
	scale: 50,
	blur: 20,

	color: 0x000000,
	background: 0xFFFFFF,
};



var vec = new Vector3();

function pattern( x, y, z, color, options, /*u, v, px, py*/ ) {

	vec.set( z, y, x );

	var dist = 1e10;
	for ( var point of options.points )
		dist = Math.min( dist, vec.distanceTo( point ) );

	var k = MathUtils.smoothstep( dist, options.minSmooth, options.maxSmooth );

	color.lerpColors( options.color, options.background, k );

}



function options( params ) {

	var data = layouts[ ( params.layout ?? defaults.layout )-1 ];

	var blur = map( params.blur??defaults.blur )**2.5 / 3;

	var scale = map( params.scale??defaults.scale, 0, data.maxScale );
	scale = ( scale / 100 )**2;

	return {

		color: new Color( params.color ?? defaults.color ),
		background: new Color( params.background ?? defaults.background ),

		points: data.points,

		minSmooth: scale - blur,
		maxSmooth: scale + blur,

		width: params.width ?? defaults.width,
		height: params.height ?? defaults.height,
	};

}




function texture( ...opt ) {

	return retexture( opt, defaults, options, pattern );

}



export { pattern, defaults, texture };
export { material } from "../texture-generator.js";
